package com.liuya.safe.user.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.liuya.base.AbstractBaseService;
import com.liuya.common.SpringUtils;
import com.liuya.safe.entitle.service.PrivilegeManagerService;
import com.liuya.safe.exception.AuthException;
import com.liuya.safe.model.SafePrivilege;
import com.liuya.safe.model.SafeRole;
import com.liuya.safe.model.SafeUserRole;
import com.liuya.safe.system.role.service.impl.RoleManagerServiceImpl;
import com.liuya.safe.user.service.UserRoleManagerService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service("userRoleManagerService")
public class UserRoleManagerServiceImpl extends AbstractBaseService<SafeUserRole> implements UserRoleManagerService {

    private String appName;
    private String userTypeName;

    @Resource(name = "privilegeManagerService")
    PrivilegeManagerService privilegeManager;
    @Resource(name = "roleManagerService")
    RoleManagerServiceImpl roleManager;

    public UserRoleManagerServiceImpl() {

    }

    public UserRoleManagerServiceImpl(String appName, String userTypeName) {
        this.appName = appName;
        this.userTypeName = userTypeName;
    }

    @Transactional
    public void assignRoles(String userId, Collection roleIds) {
        Map param = new HashMap();
        param.put("userId", userId);
        mapper.deleteBy(param);

        // create new records
        ArrayList userRoles = new ArrayList();
        Iterator itr = roleIds.iterator();
        while (itr.hasNext()) {
            String roleId = (String) itr.next();
//            userRoles.add(new SafeUserRole(userId, roleId));
            mapper.insert(new SafeUserRole(userId, roleId));
        }
//        super.batchSave(userRoles);
    }

    public void assignRoles(Collection userIds, Collection roleIds) {
        Iterator itr = userIds.iterator();
        while (itr.hasNext()) {
            Object userId = itr.next();
            assignRoles((String)userId, roleIds);
        }
    }

    public void deleteUserRoles(Object userId) {
        Map param = new HashMap();
        param.put("userId", userId);
        mapper.deleteBy(param);
    }

    public void deleteUserRoles(String roleId) {
        Map param = new HashMap();
        param.put("roleId", roleId);
        mapper.deleteBy(param);
    }

    public Collection getRoles(Object userId) {
        Map param = new HashMap();
        param.put("userId", userId);
        Collection userRoles = mapper.selectAll(param);

        // ge Privilege collection
        List roles = new ArrayList();
        Iterator itr = userRoles.iterator();
        RoleManagerServiceImpl service = (RoleManagerServiceImpl) SpringUtils.getBean("roleManagerService");
        while (itr.hasNext()) {
            String roleId = ((SafeUserRole) itr.next()).getRoleId();
            roles.add(service.selectById(roleId));
        }

        return roles;
    }

    public SafePrivilege getBusinessPrivilegeTree(Object userId) {
        // which privilege(s) this user has been assigned?
        Collection assignedPrivilegeIds = new HashSet();
        Collection roles = getRoles(userId);
        for (Iterator roleIter = roles.iterator(); roleIter.hasNext();) {
            SafeRole role = (SafeRole) roleIter.next();
            Collection privileges = roleManager.getPrivileges(role.getId());

            for (Iterator pvlgIter = privileges.iterator(); pvlgIter.hasNext();) {
                SafePrivilege pvlg = (SafePrivilege) pvlgIter.next();
                assignedPrivilegeIds.add(new Integer(pvlg.getId()));
            }
        }

        Collection allBussinessPrivileges = privilegeManager.getAllBusinessPrivileges();

        // get privilege copies (copy from allBusinessPrivilege)
        Collection assignedPrivileges = new HashSet();
        for (Iterator iter = allBussinessPrivileges.iterator(); iter.hasNext();) {
            SafePrivilege pvlg = (SafePrivilege) iter.next();

            if (!pvlg.getIsLeaf()) {
                // parent nodes, does't evel be granted now. We will delte
                // "lonely" nodes later
                boolean rootNode = pvlg.getId() == SafePrivilege.BUSINESS_PRIVILEGE_TREE_ROOT_ID;
                if (!rootNode) {
                    assignedPrivileges.add((SafePrivilege) (pvlg.clone()));
                }
            } else {
                // only add granted privilege & displayable privilege here
                if (pvlg.getDisplay() && assignedPrivilegeIds.contains(new Integer(pvlg.getId()))) {
                    assignedPrivileges.add((SafePrivilege) (pvlg.clone()));
                }
            }
        }

        SafePrivilege root = new SafePrivilege();
        root.setId(SafePrivilege.BUSINESS_PRIVILEGE_TREE_ROOT_ID);
        root.setType(SafePrivilege.BUSINESS_PRIVILEGE);
        root.setIsLeaf(false);
        root.setPid(SafePrivilege.NULL_ROOT_ID);
        root.setParent(null);

        // add root node
        assignedPrivileges.add(root);

        // buildTree. A litter difference from PrivilegeManagerImpl#buildTree()
        buildTree(assignedPrivileges);

        // delete lonely nodes (they are parent nodes :) )
        reduceTree(assignedPrivileges);
        return root;
    }

    private void reduceTree(Collection assignedPrivileges) {
        for (Iterator iter = assignedPrivileges.iterator(); iter.hasNext();) {
            SafePrivilege pvlg = (SafePrivilege) iter.next();
            checkNode(pvlg);
        }
    }

    private void checkNode(SafePrivilege pvlg) {
        // leaf and root node needn't to be checked
        if (pvlg.getIsLeaf() || pvlg.getId() == SafePrivilege.BUSINESS_PRIVILEGE_TREE_ROOT_ID) {
            return;
        }

        // if a parent has no child nodes, we call it "lonely node". Delete it
        Collection children = pvlg.getChildren();
        if (children == null || children.size() == 0) {
            SafePrivilege parent = (SafePrivilege) pvlg.getParent();
            parent.getChildren().remove(pvlg);

            // When deleted, the node's parent comes to be a "lonely node", so
            // check parent again
            checkNode(parent);
        }
    }

    private void buildTree(Collection assignedPrivileges) {
        Map map = new HashMap();

        for (Iterator iter = assignedPrivileges.iterator(); iter.hasNext();) {
            SafePrivilege child = (SafePrivilege) iter.next();
            map.put(new Integer(child.getId()), child);
        }

        for (Iterator iter = assignedPrivileges.iterator(); iter.hasNext();) {
            SafePrivilege child = (SafePrivilege) iter.next();
            SafePrivilege parent = (SafePrivilege) map.get(new Integer(child.getPid()));
            if (parent != null) {
                parent.getChildren().add(child);
                child.setParent(parent);
            }
        }

        Comparator comp = new Comparator() {
            public int compare(Object arg0, Object arg1) {
                SafePrivilege p0 = (SafePrivilege) arg0;
                SafePrivilege p1 = (SafePrivilege) arg1;
                return p0.getOrderNum() - p1.getOrderNum();
            }
        };

        // sort by orderNum
        for (Iterator iter = assignedPrivileges.iterator(); iter.hasNext();) {
            SafePrivilege p = (SafePrivilege) iter.next();
            Collection children = p.getChildren();

            if (children != null) {
                Collections.sort((List) children, comp);
            }
        }
    }

    public boolean hasPrivilege(Object userId, String pvlgId) {
        SafePrivilege privilege = privilegeManager.getPrivilege(pvlgId);
        if (privilege == null) {
            throw new AuthException("No privilege with id='" + pvlgId + "' found.");
        }

        if (privilege.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
            // NON_ROLE_PRIVILEGE don't need to be check
            return true;
        }

        boolean hasPrivilege = false;
        Iterator itr = getRoles(userId).iterator();
        while (itr.hasNext()) {
            SafeRole role = (SafeRole) itr.next();
            hasPrivilege = roleManager.hasPrivilege(role.getId(), pvlgId);
            if (hasPrivilege)
                break;
        }
        return hasPrivilege;
    }

}
